\documentclass[12pt,a4paper]{article}
\usepackage[polish]{babel}
\usepackage[utf8]{inputenc}
\usepackage{polski}
\usepackage[T1]{fontenc}
\usepackage{ae,aecompl}
\frenchspacing
\usepackage[margin=2cm]{geometry}
\usepackage{graphicx}
\usepackage{float}
\usepackage{hyperref}
\usepackage{amsmath}
\usepackage{listings}
\usepackage{color}
\lstset{
        basicstyle=\ttfamily,
        language=Java,
        frame=single,
        keywordstyle=\color[rgb]{0,0,1},
        commentstyle=\color[rgb]{0.133,0.545,0.133},
        stringstyle=\color[rgb]{0.627,0.126,0.941},
        columns=flexible,
}

\begin{document}
\title{\LARGE  Projekt nr 14 \\ \vspace{0.4cm} \textbf{DEFORMACJE GEOMETRYCZNE}}
\author{Michał Drobniak, 
 Marcin Piłat, Adam Syrek }
\date{Styczeń 2013}
\maketitle

\vfill
\begin{figure}[H]
\begin{center}
\includegraphics[width=0.7\textwidth]{agh_nzw_s_pl_1w_wbr_rgb_150ppi.jpg}
\end{center}
\end{figure}
\newpage

\large
\section{Tytuł projektu i autorzy projektu}
Aplikacja \textbf{Jasio - Deformacje geometryczne} stworzona została z myślą o udostępnieniu użytkownikowi, różnych sposobów geometrycznej deformacji swojego obrazu. Sposób obsługi programu został na tyle uproszczony, że powinien być jasny już od początku korzystania z programu. \\
\\
Projekt zrealizowany został przez 3 osobowy zespół:
\begin{itemize}
	\item Michał Drobniak - praca nad algorytmami i GUI procesów oraz dokumentacja
	\item Marcin Piłat - praca nad algorytmami i GUI procesów oraz główny kontroler z otwarciem i zapisem obrazu oraz głównym GUI
	\item Adam Syrek - praca nad algorytmami i oraz panel obrazu z interpolacją bilinearną oraz klasa pomocnicza DraggableCircle
\end{itemize}

\section{Opis projektu}
Ponieważ obrazy rejestrowane w życiu codziennym przez urządzenia mogą być zniekształcone przez różnorodne czynniki, potrzebna jest możliwość korekcji takich deformacji, aby przywrócić obraz do prawdziwego kształtu. Korzystając z przekształceń liniowych oferowanych przez geometrię oraz składanie macierzowe tych przekształceń, możliwe jest przywrócenie rzeczywistej formy obrazu. Przykładowe deformacje obrazu, jakie oferuje program to np. dostosowania rozmiaru  do potrzebnego, obrócenie posiadanego obrazu o określony kąt albo osiągnięcie efektu pochyleń w różnych płaszczyznach.\\
 

\section{Założenia wstępne przyjęte w realizacji projektu}
\paragraph{Realizacja podstawowych założeń programu zgodnie z wymaganiami:}
\begin{itemize}
	\item Wczytanie obrazu w formatach .jpeg, .jpg, .png, .gif
	\item Zapis obrazu do formatów .jpeg, .jpg, .png, .gif, zapisany zostanie obraz poddany kolejnym deformacjom z rozmiarami ustalonymi przez dany proces.
	\item Do obsługi parametrów każdego procesu stworzone zostaje nowe okno dialogowe zawierające elementy GUI zarządzające wartościami ustawień oraz zmniejszony podgląd obrazu na którym dokonywane są operacje tymczasowe, dzięki czemu możliwa jest obserwacja zmian w zależności od przyjętych parametrów bez konieczności faktycznej zmiany załadowanego obrazu
	\item Rotacja obrazu o dowolny kąt wokół osi prostopadłej do płaszczyzny rejestrującej
	\item Przechylenie w pionie i poziomie
	\item Obrót wokół osi leżącej w płaszczyźnie rejestrującej	
	\item Korekta soczewki przy użyciu współczynników a,b,c
\end{itemize}
\paragraph{Realizacja rozszerzonych założeń programu:}
\begin{itemize}
	\item Zmiana rozmiarów obrazu w pionie i poziomie	
	\item Perspektywa w dowolnej osi
	\item Zniekształcenie lokalne
\end{itemize}


\section{Analiza projektu}
\subsection{Specyfikacja danych wejściowych}
Dane wejściowe wymagane przez aplikację to załadowany przez użytkownika wybrany obraz oraz wybrane w konkretnym oknie dialogowym parametry deformacji. Każde okno dialogowe procesu zawiera tylko elementy odpowiadające ustawieniom potrzebnym do danej deformacji.

\subsection{Opis oczekiwanych danych wyjściowych}
Możliwe dane wyjściowe programu to jedynie zdeformowany przez użytkownika odpowiednim procesem obraz. Po danej operacji obraz wyświetlony jest w oknie głównym programu, w którym możliwe jest zobaczenie ostatecznych efektów deformacji i zdecydowanie czy jest on gotowy do zapisu. Końcowym efektem działania aplikacji jest zapisany plik we wcześniej zdefiniowanych formatach.

\subsection{Zdefiniowanie struktur danych}
Ładowany obraz przechowywany jest w obiekcie typu BufferedImage. Obiekt ten, charakteryzuje się tym, że opisuje obraz przez ogólnie dostępny bufor z jego danymi, dzięki czemu łatwo podlega modyfikacji. Przy tworzeniu obiektów tego typu możliwe jest wybranie w jaki dokładnie sposób, będzie reprezentowany obraz. Typ \textit{TYPE\_INT\_ARGB} oznacza że będzie on reprezentowany przez 8-bitowe kolorowe komponenty RGBA (podstawowy model kolorów ze współczynnikiem przeźroczystości) spakowane do pikseli typu Integer.

\subsection{Specyfikacja interfejsu użytkownika}
Interfejs użytkownika został uproszczony do jednego okna z wyświetlającymi się odpowiednimi oknami dialogowymi. Do wyboru deformacji służy górna belka menu, element \textbf{przekształcenia}. Wprowadzony został taki układ, aby jak najwięcej miejsca zostawić na główny podgląd przetwarzanego obrazu.
\begin{figure}[H]
\begin{center}
\includegraphics[width=0.7\textwidth]{gui_1.png}
\caption{Główne okno aplikacji}
\end{center}
\end{figure}

\begin{figure}[H]
\begin{center}
\includegraphics[width=0.7\textwidth]{gui_2.png}
\caption{Główne okno aplikacji z możliwymi przekształceniami do wyboru}
\end{center}
\end{figure}

Wybierając konkretne przekształcenia, pojawia się odpowiednie okno z dostosowanymi elementami do ustawiania parametrów.
\begin{figure}[H]
\begin{center}
\includegraphics[width=0.5\textwidth]{gui_3.png}
\caption{Widok okna dialogowego konkretnego procesu - przypadek perspektywy}
\end{center}
\end{figure}

\begin{figure}[H]
\begin{center}
\includegraphics[width=0.55\textwidth]{gui_4.png}
\caption{Widok okna dialogowego konkretnego procesu - przypadek rotacji}
\end{center}
\end{figure}

\subsection{Wyodrębnienie i zdefiniowanie zadań}
Projekt został podzielony na części:
\begin{itemize}
	\item Stworzenie prostego okna zawierającego górne menu oraz panel obrazu
	\item Dodanie możliwości wczytania i zapisu do pliku
	\item Utworzenie dla każdego procesu klasy obsługującej operację oraz klasę okna dialogowego
	\item Wybranie dla każdej deformacji odpowiedniego algorytmu i jego implementacja
	\item Obsługa parametrów i podglądu w klasie dialogowej
	\item Prezentacja wynikowego obrazu w głównym oknie
\end{itemize}
Najważniejsze części, tzn. obliczenia według przyjętych algorytmów oraz komunikacja z użytkownikiem zawierają się w dwóch klasach. Ostateczna prezentacja następuje na panelu również opisanym klasą

\subsection{Decyzja o wyborze narzędzi programistycznych}
Program został napisany w języku Java dla JRE w wersji 1.6 przy użyciu środowiska NetBeans. Wybór ten został podyktowany dobrą i elastyczną obsługą obrazów przez ten język (dzięki klasie BufferedImage) oraz łatwości w dostosowaniu interfejsu użytkownika do różnych wymagań (każdy proces wymaga innego okna dialogowego).\\
Do uruchomienia programu jest więc potrzebne środowisko JRE co najmniej w \textbf{wersji 1.6.0}.


\section{Podział pracy i analiza czasowa}

%\large
\begin{tabular}{l | l | l}
	\textbf{Do zrealizowania} & \textbf{Biorący udział} & \textbf{Potrzebny czas} \\ \hline
	Przygotowanie diagramu klas & Drobniak,Piłat,Syrek & 1 tydzień \\ \hline
	Stworzenie głównego GUI\\ oraz obsługa obrazu & Piłat,Syrek & 2 dni \\ \hline
	Utworzenie podstawowych\\ klas procesów i klas dialogowych & Drobniak,Piłat,Syrek & 5 dni \\ \hline
    Dodanie dodatkowych procesów & Piłat,Syrek & 1 tydzień \\ \hline
    Utworzenie dokumentacji\\ przygotowanych procesów i \\całej aplikacji & Drobniak & 3 dni \\ \hline
    Testowanie działania wszystkich\\ procesów i poprawa błędów & Drobniak,Piłat,Syrek & 2 dni \\
\end{tabular}
\newline \newline \newline
Takie etapy pracy wydają się logiczne i zgodne z przyjętymi zasadami tworzenia oprogramowania

\section{Opracowanie i opis niezbędnych algorytmów}
Algorytmy dotyczące procesów, działają na zasadzie \textbf{mapowania odwrotnego}, tzn. że dla każdego piksela z obrazu wynikowego, dopasowywany jest piksel z obrazu źródłowego. Dzięki tej metodzie każdy piksel w obrazie wynikowym ma odpowiednią wartość. 
\begin{figure}[H]
\begin{center}
\includegraphics[width=0.45\textwidth]{inverse_mapping.png}
\caption{Idea mapowania odwrotnego}
\end{center}
\end{figure} 
\subsection{Interpolacja bilinearna}
Algorytm interpolacji bilinearnej (dwuliniowej) jest algorytmem używanym w celu poprawienia jakości obrazu wynikowego. Każdy piksel obrazu wynikowego przyjmuje wartość na podstawie wartości czterech sąsiednich punktów obrazu wejściowego. Algorytm nazywany jest dwuliniowym gdyż wykorzystuje interpolację liniową w pionie i w poziomie. \\
Jego zadaniem jest dopasowanie wartości w danym punkcie, wykorzystując do tego cztery punkty sąsiadujące znajdujące się w narożnikach kwadratu otaczającego dany punkt. Użycie tego algorytmu wynika z faktu, że dla danego piksela (x,y) na obrazie wynikowym, piksel odpowiadający mu na obrazie oryginalnym może mieć współrzędne niecałkowite. Interpolację dwuliniową przeprowadza się z odpowiednią wagą dla współrzędnej X i Y (wartość dokładna - wartość zaokrąglona w dół osobno dla X i Y). Interpolacji poddaje się każdą składową koloru osobno.
\begin{figure}[H]
\begin{center}
\includegraphics[width=0.3\textwidth]{alg_1.png}
\end{center}
\end{figure}
Najpierw przeprowadzana jest interpolacja liniowa wzdłuż osi X:
\begin{equation}
f(R_1)\approx \frac{x_2-x}{x_2-x_1}f(Q_{11}) + \frac{x-x_1}{x_2-x_1}f(Q_{21}) ,R_1=(x,y_1)
\end{equation}
\begin{equation}
f(R_1)\approx \frac{x_2-x}{x_2-x_1}f(Q_{12}) + \frac{x-x_1}{x_2-x_1}f(Q_{22}) ,R_1=(x,y_2)
\end{equation}
Następnie wzdłuż OY:
\begin{equation}
f(P)\approx \frac{y_2-y}{y_2-y_1}f(R_1) + \frac{y-y_1}{y_2-y_1}f(R_2)
\end{equation}
\\
\subsection{Algorytm zmiany rozmiaru}
Jest to prosty algorytm przeskalowania obrazu wykorzystujący stosunek pierwotnych rozmiarów do nowych.
\begin{center}
$xRatio=\frac{oldWidth}{newWidth}$ \\
$yRatio=\frac{oldHeight}{newHeight}$ \\
\end{center}
Nową współrzędną piksela będzie iloraz dotychczasowej współrzędnej i wyliczone stosunku.

\subsection{Algorytm rotacji 3D}
Algorytm rotacji 3D pozwala na rotację obrazu we wszystkich osiach przestrzeni. Używane w tym celu są standardowe macierze rotacji osi X, Y, Z we współrzędnych homogenicznych. Punkt na obrazie po przekształceniu odpowiadający punktowi na obrazie przed przekształceniem wyliczany jest poprzez inwersję złożenia tych trzech macierzy. Następnie punkty dodatkowo poddawane są projekcji perspektywicznej. 

\subsection{Algorytm perspektywy}
Algorytm perspektywy wykorzystuje transformację perspektywiczną, która wymaga wyliczenia współczynników A-I (macierz 3x3). Aby wyliczyć te współczynniki należy najpierw znaleźć współczynniki pomocnicze, które zależą od tego, czy transformacja jest afiniczna. Robimy to w następujący sposób:
\begin{equation}
\begin{matrix}
dx_{1}=x_{1}-x_{2} & dx_{2}=x_{3}-x_{2} & dx_{3}=x_{0}-x_{1}+x_{2}-x_{3} \\
dy_{1}=y_{1}-y_{2} & dy_{2}=y_{3}-y_{2} & dx_{3}=y_{0}-y_{1}+y_{2}-y_{3}
\end{matrix}
\end{equation}
Jeśli $dx_{3}=0$ oraz $dy_{3}=0$ to przekształcenie jest afiniczne i współczynniki pomocnicze liczone są następująco:
\begin{eqnarray}
a_{11}&=&x_{1}-x_{0} \nonumber \\
a_{21}&=&x_{2}-x_{1} \nonumber \\
a_{31}&=&x_{0} \nonumber \\
a_{12}&=&y_{1}-y_{0} \nonumber \\
a_{22}&=&y_{2}-y_{1} \nonumber \\
a_{32}&=&y_{0} \nonumber \\
a_{13}&=&a_{23}=0 \nonumber \\
a_{33}&=&1 \nonumber 
\end{eqnarray}
W przeciwnym wypadku, jeśli przekształcenie nie jest afiniczne:
\begin{eqnarray}
a_{11}&=&x_{1}-x_{0}+a_{13}x_{1} \nonumber \\
a_{21}&=&x_{2}-x_{1}+a_{23}x_{3} \nonumber \\
a_{31}&=&x_{0} \nonumber \\
a_{12}&=&y_{1}-y_{0} +a_{13}y_{1}\nonumber \\
a_{22}&=&y_{2}-y_{1}+a_{23}y_{3}\nonumber \\
a_{32}&=&y_{0} \nonumber \\
a_{13}&=&\frac{dx_{3}dy_{2}-dx_{2}dy_{3}}{dx_{1}dy_{2}-dy_{1}dx_{2}} \nonumber \\
a_{23}&=&\frac{dx_{1}dy_{3}-dx_{3}dy_{1}}{dx_{1}dy_{2}-dy_{1}dx_{2}} \nonumber \\
a_{33}&=&1 \nonumber 
\end{eqnarray}
Następnie na podstawie tych współczynników wyliczane są współczynniki A-I:
\begin{eqnarray}
A&=&a_{22}a_{33}-a_{23}a_{32} \nonumber \\
B&=&a_{23}a_{31}-a_{21}a_{33}\nonumber \\
C&=&a_{21}a_{32}-a_{22}a_{31}\nonumber \\
D&=&a_{13}a_{32}-a_{12}a_{33}\nonumber \\
E&=&a_{11}a_{33}-a_{13}a_{31}\nonumber \\
F&=&a_{12}a_{31}-a_{11}a_{32}\nonumber \\
G&=&a_{12}a_{23}-a_{13}a_{22}\nonumber \\
H&=&a_{13}a_{21}-a_{11}a_{23}\nonumber \\
I&=&a_{11}a_{22}-a_{12}a_{21} \nonumber
\end{eqnarray}
Mając te współczynniki można wyliczyć funkcję przejścia dla odwrotnego mapowania:
\begin{equation}
x_{org}=\frac{Ax_{wy}+By_{wy}+C}{Gx_{wy}+Hy_{wy}+I} \quad , \quad y_{org}=\frac{Dx_{wy}+Ey_{wy}+F} {Gx_{wy}+Hy_{wy}+I} \nonumber
\end{equation}

\subsection{Algorytm korekcji soczewki}
Zniekształcenie soczewki odwzorowuje wielomian trzeciego stopnia zdefiniowany następująco:
\begin{equation}
r_{org}=(ar^{3}_{wy}+br^{2}_{wy}+cr_{wy}+d)r_{wy} \nonumber
\end{equation}
W powyższym wzorze $r_{org}$ to odległość punktu od środka obrazu oryginalnego, analogicznie $r_{wy}$ dla obrazu wyjściowego. Parametry $a,b,c$ to parametry korekty soczewki, parametr $d$ jest wyliczany na podstawie pozostałych trzech parametrów $d=1-(a+b+c)$ po to, by zachować taki sam rozmiar wyjściowy obrazu. Parametry korekty soczewki są bardzo małe, w większości wypadków poniżej $0.01$, a nawet o kilka rzędów wielkości mniejsze. Mając punkty $(x_{wy},y_{wy})$ możemy łatwo dokonać mapowania odwrotnego. Najpierw obliczamy promień $r_{wy}$:
\begin{equation}
r_{wy}=\sqrt{(x_{c}-x_{wy})^{2}+(y_{c}-y_{wy})^2} \nonumber
\end{equation}
gdzie $x_{c},y_{c}$ to współrzędne środka obrazu. Następnie obliczamy stosunek $f=\frac{r_{wy}}{r_{org}}$ i na końcu obliczamy współrzędne $(x,y)$ na obrazie oryginalnym:
\begin{equation}
x_{org}=x_{c}-(x_{c}-x_{wy})f \quad , \quad y_{org}=y_{c}-(y_{c}-y_{wy})f \nonumber
\end{equation}

\subsection{Algorytm rotacji}
Program zgodnie z założeniami ma umożliwiać rotację obrazu o wybrany kąt wokół dowolnej osi prostopadłej do płaszczyzny rejestrującej. Wyliczenie położenia piksela w tej operacji musi być więc złożeniem kolejnych przekształceń: translacji o wektor centrum rotacji, samej rotacji oraz ponownej translacji do pierwotnego centrum.
\begin{lstlisting}
x=sin(angle)*(x-centerX) - cos(angle)*(y-centerY) + centerX
y=sin(angle)*(y-centerY) + cos(angle)*(x-centerX) + centerY
\end{lstlisting}
Dla obrotów o $90^\circ$ i $180^\circ$ proces rotacji korzysta z o wiele bardziej optymalnych operacji:
\begin{itemize}
	\item Dla $90^\circ$, wysokość obrazu staje się jego szerokością, szerokość zatem wysokością a pozycje pikseli są po prostu zamieniane:
	\begin{center}
		 x=y\\ y=width-x.
	\end{center}
	\item Dla $180^\circ$, rozmiary pozostają takie same, zamieniane są tylko piksele:
	\begin{center}
		 x=x\\ y=height-y.
	\end{center}
\end{itemize}

\subsection{Algorytm pochylenia}
Deformacja dokonująca pochylenia obrazu wzdłuż osi X bądź Y. Współrzędne wyliczane ze wzorów:\\
xa - kąt dla x, ya - kąt dla y \\
\begin{center}
$x=tan(xa)\frac{y-tan(xa)x}{1-tan(xa)tan(ya)}$ 
$y=\frac{y-tan(ya)x}{1-tan(xa)tan(ya)}$
\end{center}
\subsection{Algorytm zniekształcenia lokalnego}
Algorytm ten opera się tylko na zaimplementowaniu funkcji mapowania odwrotnego na \textbf{ograniczonym obszarze} obrazu oryginalnego. Nie powoduje on zmian wielkości obrazu (może jedynie zakrzywiać krawędzie obrazu). Obszar przekształcenia jest ograniczony do koła o promieniu wybranym przez użytkownika. Algorytm przeciąga pixel z jednego miejsca w inne (dwa kliknięcia myszką) i wszystkie okoliczne pixele 'przeciągają się' za wybranym pixelem.
\begin{figure}[H]
\begin{center}
\includegraphics[width=0.40\textwidth]{warp_before.png}
\caption{Przed zniekształceniem}
\end{center}
\end{figure} 
\begin{figure}[H]
\begin{center}
\includegraphics[width=0.45\textwidth]{warp_after.png}
\caption{Po zniekształceniu}
\end{center}
\end{figure} 
Funkcja przekształcenia jest oparta na wzorze znalezionym w internecie, następnie przekształconym metodą prób i błędów:
\begin{center}
$\vec{x}_{org} = \vec{x}_{wy} - \left({ r^2 - |\vec{x}_{wy} - \vec{c}|^2 \over r^2 + |\vec{x}_{wy} - \vec{c}|^2 + |\vec{m} - \vec{c}|^2 }\right)^2(\vec{m} - \vec{c})$
\end{center}
Gdzie:
\begin{itemize}
\item $\vec{x}_{org}$ - to położenie pixela na obrazie oryginalnym
\item $\vec{x}_{wy}$ - to położenie pixela na obrazie wynikowym
\item $r$ - to promień zniekształcenia
\item $\vec{c}$ - to położenie początku zniekształcenia (pierwsze kliknięcie)
\item $\vec{m}$ - to położenie końca zniekształcenia (drugie kliknięcie)
\end{itemize}
Ponadto ta funkcja jest wykonywana tylko na pixelach które znajdują się w odległości od początku zniekształcenia ($\vec{c}$) mniejszej od $r$. Pozostałe pixele pozostają takie same.

 
\newpage
\section{Kodowanie}
Opis stworzonych klas i ich najważniejszych metod oraz wykorzystane wzorce projektowe. 
\subsection{Diagram klas projektu:}
\begin{figure}[H]
\begin{center}
\includegraphics[width=1.0\textwidth]{cd_1.png}
\caption{Diagram klas projektu}
\end{center}
\end{figure}

Kod programu podzielony jest na dwie naturalne części: klasy \textbf{procesów i ich okien dialogowych} oraz klasy \textbf{pomocnicze} (wraz z GUI).\\
\\
\textbf{Krótka prezentacja klas  pomocniczych:}\\
\\
Klasa \textbf{AppController} sterująca głównym oknem programu oraz wywołaniem wybranych procesów wraz z prezentacją uzyskanego efektu w głównym panelu. Tutaj również jest realizowana funkcjonalność wczytywania i zapisu obrazów.
\begin{lstlisting}
class AppController {
...
	void process(){
        image = proc.process(image);
        image = proc.clippImage(image);
        panel.setImage(image);
        gui.repaint();
    }
...
}
\end{lstlisting}

Klasa \textbf{AppGui} przechowuje wszystkie elementy graficznego interfejsu użytkownika.
\\

Klasa \textbf{ImagePanel} jest wykorzystywana jako główny podgląd przetwarzanego obrazu oraz wykorzystana w każdym oknie dialogowym aby pokazać zmniejszony podgląd. \\
Metoda getSmallImagePanel(), pełniąca rolę nazwanego konstruktora, służy do wytworzenia zmniejszonego podglądu, zmieniając rozmiar tymczasowego obrazu do wyznaczonej maksymalnej szerokości albo wysokości. Wykorzystuje do tego jeden z istniejących procesów \textbf{Resize}.
\begin{lstlisting}
class ImagePanel {
...
	static ImagePanel getSmallImagePanel(BufferedImage img){
        ResizeProcess resize = new ResizeProcess();
        double w = img.getWidth();
        double h = img.getHeight();
        double prop = w/h;
        double nw,nh;
        if(w>350 || h> 350){
            if(w>h){
                nw = 350;
                nh = nw/prop;
            } else {
                nh = 350;
                nw = nh*prop;
            }
        } else {
            nw = w;
            nh = h;
        }
        resize.setNewSize((int)nw, (int)nh);
        BufferedImage image = resize.process(img);
        ImagePanel panel = new ImagePanel();
        panel.setImage(image);
        panel.setSize((int)nw, (int)nh);
        return panel;
    }
...
}
\end{lstlisting}

Klasa \textbf{DraggableCircle} tworzy okrąg w zadanym punkcie (x,y) o określonym rozmiarze. Użyta w niektórych procesach do graficznego uproszczenia obsługi.\\

Klasa \textbf{WarpMemento}, będąca klasą pomocniczą procesu Warp, pozwala na zapamiętywanie kolejnych zniekształceń wykonywanych w oknie dialogowym, dzięki czemu przy ostatecznej deformacji oryginalnego obrazu, wszystkie zniekształcenia nakładają się na siebie. Klasa ta umożliwia również cofanie wykonanego przekształcenia. Wykorzystany został tu wzorzec projektowy \verb+Memento+ .
\begin{lstlisting}
class WarpMemento {
    Vector<Point> startPoints = new Vector<Point>();
    Vector<Point> endPoints = new Vector<Point>();
    Vector<Integer> rads = new Vector<Integer>();
    ...
    void addPoints(int x1, int y1, int x2, int y2, int r){
        startPoints.add(new Point(x1, y1));
        endPoints.add(new Point(x2, y2));
        rads.add(r);
    }
    ...
}
\end{lstlisting}
%{lstlisting}
\newpage
\textbf{Klasy procesów:}\\
\\
Klasa \textbf{ImageProcess} jest klasą abstrakcyjną, po której dziedziczą wszystkie klasy obsługujące procesy. Sama zawiera metody służące do przeprowadzenie procesu i uzyskania współrzędnych obrazu wynikowego oraz do np. interpolacji dwuliniowej lub przycięcia obrazu z pustego obszaru. \\
Metoda interpolacji dwuliniowej przyjmuje jako parametry wagi współrzędnych x i y oraz wartości RGB 4 pikseli otaczających (wyjaśnione w punkcie 6.1). \\
Metoda przycięcia obrazu przyjmuje jako parametr jego referencję i sprawdza na podstawie wypełnienia kolorów czy wynikowy obraz potrzebuje przycięcia.\\
Metoda getOriginalCoordinates(x,y,out[]) wywoływana jest zawsze w metodzie process() każdego obiektu procesu, i jej zadaniem jest wykonanie mapowania odwrotnego, korzystając z przekształceń zdefiniowanych przez odpowiadający algorytm.
\\
Działanie wykonania procesu jest następujące:
\begin{enumerate}
	\item W klasie dialogowej, po akcji użytkownika, zbierane są dane o parametrach procesu i ustawiane na obiekcie \textbf{proc}, będącym obiektem procesu odpowiadającego klasie dialogowej
	\item 
	\begin{enumerate}
		\item Jeśli działanie ma odbyć się tylko na podglądzie to następuje wywołanie metody \textbf{process()} obiektu \textbf{proc}.
		\item Jeśli deformacja ma mieć miejsce na oryginalnym obrazie to konieczne jest ustawienie w obiekcie \textbf{kontrolera} właściwego procesu metodą\\ \textbf{setProcess(\textit{obiekt typu ImageProcess})} a następnie wywołanie metody \textbf{process} na obiekcie kontrolera.
	\end{enumerate}	
\end{enumerate}
\begin{lstlisting}
class ImageProcess {
	...
	protected int bilinearInterpolation(double xw, double yw, 
	  int nw, int ne, int sw, int se) {
        double m0, m1;
        int a0 = (nw >> 24) & 0xff;
        int r0 = (nw >> 16) & 0xff;
        int g0 = (nw >> 8) & 0xff;
        int b0 = nw & 0xff;
        int a1 = (ne >> 24) & 0xff;
        int r1 = (ne >> 16) & 0xff;
        int g1 = (ne >> 8) & 0xff;
        int b1 = ne & 0xff;
        int a2 = (sw >> 24) & 0xff;
        int r2 = (sw >> 16) & 0xff;
        int g2 = (sw >> 8) & 0xff;
        int b2 = sw & 0xff;
        int a3 = (se >> 24) & 0xff;
        int r3 = (se >> 16) & 0xff;
        int g3 = (se >> 8) & 0xff;
        int b3 = se & 0xff;

        double cx = 1.0f - xw;
        double cy = 1.0f - yw;

        m0 = cx * a0 + xw * a1;
        m1 = cx * a2 + xw * a3;
        int a = (int) (cy * m0 + yw * m1);

        m0 = cx * r0 + xw * r1;
        m1 = cx * r2 + xw * r3;
        int r = (int) (cy * m0 + yw * m1);

        m0 = cx * g0 + xw * g1;
        m1 = cx * g2 + xw * g3;
        int g = (int) (cy * m0 + yw * m1);

        m0 = cx * b0 + xw * b1;
        m1 = cx * b2 + xw * b3;
        int b = (int) (cy * m0 + yw * m1);
        return (a << 24) | (r << 16) | (g << 8) | b;
    }
	...
    BufferedImage clippImage(BufferedImage img){
        ...
    }
}
\end{lstlisting}
\newpage
\textbf{Spis klas procesów wraz z klasami dialogowymi:}
\begin{itemize}
	\item LensCorrectionProcess, LensCorrectionDialog
	\item PerspectiveProcess, PerspectiveDialog
	\item ResizeProcess, ResizeDialog
	\item Rotation3DProcess, Rotation3Dialog
	\item RotationProcess, RotationDialog
	\item SkewProcess, SkewDialog
	\item WarpProcess, WarpDialog
\end{itemize}
Ich implementacja to w głównej mierze zastosowanie algorytmów które zostały opisane w punkcie 6, prezentowanie ich kodu jest zbędne.

\section{Testowanie}
Zadaniem aplikacji jest zdeformowanie obrazu lub przywrócenie go do pożądanej postaci po pewnej deformacji. Przetestowanie więc poprawności w pierwszym przypadku jest oparte na dotychczasowym doświadczeniu z obserwacji efektów działania danego procesu oraz wniosków z wyników algorytmów.

W drugim przypadku, możliwe jest faktyczne zauważenie czy deformacja się powiodła, gdyż oceny naturalności danego obrazu może dokonać każdy prawidłowo widzący.

Testowanie działania aplikacji zostało sprawdzone również pod względem obsługi różnych formatów i rozmiarów zdjęć. Polegając jednak na uniwersalności algorytmów udało się potwierdzić że cechy te nie mają większego wpływu na efekt deformacji.

Tak więc testy zostały przeprowadzone na kolejnych obrazach:
\begin{itemize}
	\item 50x50, jpg,png,gif
	\item 200x50, jpg,png,gif
	\item 50x200, jpg,png,gif
	\item 500x500, jpg,png
	\item 800x600, jpg
	\item 2300x3500, jpg,png
\end{itemize}

\section{Wdrożenie, raport i wnioski}
Aplikacja została uruchomiona pod systemami Windows 8, Windows 7, Windows XP, Linux w środowisku JRE w wersji co najmniej 1.6. Dane początkowe, tzn. obraz, były pobierane z różnych źródeł i różniły się zawartością. Nie stwierdzono efektów ubocznych używania w różnych systemach dla zróżnicowanych danych wejściowych.

\subsection{Raport wykonania:}

\textbf{Założenia podstawowe i rozszerzone udało się osiągnąć.}\\
Jedyne problemy które zostały nie do końca rozwiązane:
\begin{itemize}
	\item Proces rotacji - założenie obrotu wokół dowolnej osi w efekcie okazało się nie przydatne, gdyż chcąc obserwować rotację na podglądzie musimy obraz zawsze przesunąć do pozycji umożliwiającej zobaczenie nie przyciętego obrazu wynikowego co niweluje efekt rotacji wokół wybranej osi.
\end{itemize}

\subsection{Możliwości rozbudowy:}

Struktura programu została utworzona w taki sposób, aby była łatwo rozszerzalna o kolejne procesy. Wystarczy stworzyć nową klasę procesu oraz okna dialogowego dla niej i dodać wybór do menu w GUI. Rozszerzalność możliwości aplikacji jest więc zależna tylko od umiejętnej implementacji algorytmu.

Inne możliwości rozbudowy to dodanie innej wersji językowej (np. angielskiej) lub dodanie obsługi nie popularnych formatów zdjęć, używanych głównie przez fotografów i grafików.

\end{document}
